<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
<script>
  // new原理
  !function () {
    function _new(fn) {
      let obj =  Object.create(fn.prototype)
      return function () {
        let tmp = fn.apply(obj, arguments)
        if (typeof tmp === 'object') {
          obj = tmp
        }
        return obj
      }
    }

    function Apple() {
      this.color = 'new'
      return 123
    }

    const apple = new Apple()
    const apple2 = _new(Apple)()
    console.log(apple, 'a0')
    console.log(apple2, 'a2')

  }()

  // call apply 原理

  !function () {

    function _call(fn) {
      return function () {
        let [obj, ...args] = arguments
        obj.__tmpFn__ = fn
        obj.__tmpFn__(...args)
        delete obj.__tmpFn__
      }
    }

    function _apply(fn) {
      return function () {
        let [obj, args] = arguments
        obj.__tmpFn__ = fn
        obj.__tmpFn__(...args)
        delete obj.__tmpFn__
      }
    }

    let obj = {
      x: 1
    }

    function test(a, b, c, d) {
      this.x = 'apply or call'
      console.log(a, b, c, d)
    }

    // test.call(obj, 1, 2, 3)
    // console.log(obj, 'call')
    // _call(test)(obj, 1, 2, 3)
    // console.log(obj, 'call - self')

    // test.apply(obj, [1, 2, 3], 5)
    // console.log(obj, 'apply')
    _apply(test)(obj, [1, 2, 3], 5)
    console.log(obj, 'apply - self')
  }()

  // bind 原理

  !function () {

    function _bind(fn) {
      return function (obj) {
        obj.__tmpFn__ = fn
        return function () {
          obj.__tmpFn__(...arguments)
          delete obj.__tmpFn__
        }
      }
    }

    let obj = {
      x: 2
    }

    function test (flag) {
      this.x = 'bind:=>' + flag
    }
    test.bind(obj)('origin')
    console.log(obj, 'bind')
    let newTest = _bind(test)(obj)
    newTest('self')
    console.log(obj, 'bind - self')
  }()

</script>
</body>
</html>
